iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0
Software Development

Unity黑科技揭秘:30個專業遊戲開發者必知的開發技巧系列 第 3

Unity 生命週期 - 一場向死而生的旅程

  • 分享至 

  • xImage
  •  

flow

讓我們從Unity文檔抓一張完整的生命週期圖。

生 老 病 死 - 是人類的生命週期。
我們從一個人年輕到死亡的生命過程中
看懂一個人的一生。

在學習任何程式之前,
我們也要先明白屬於它的生命週期。

在 Unity 中,
天地之初,一切的故事
從Awake開始:

  • Awake:執行後第一個被執行的函數。
  • OnEnable:物體被啟用時執行。
  • Start:第一次畫面更新之前執行。

Scripts Execution Order:
但是有那麼多個Start,
要怎麼判斷哪個Start先執行呢?
你可以去Project Settings -> Scripts Execution Order 調整順序:

當天地初始化後
四季開始自己不斷運行:

  • Update:每一幀(每一秒鐘中的一個畫面)都會執行一次。
  • FixedUpdate:執行的頻率比 Update 更高,通常用來處理物理運算。
  • LateUpdate:在所有 Update 函數執行完之後執行,用來處理攝影機的移動。

Coroutine:
協程Coroutine 可以讓我們在主要工作中偷懶去泡一杯咖啡。

yield WaitForSeconds:等待X秒的時間。
yield StartCoroutine:等待另一個人泡完咖啡的時間。

如果你不是用Coroutine,
而是使用Async, Task的方式泡咖啡
要注意更新UI畫面的時候要切換回主要的線程:

Unity Main Thread Dispatcher
強制切回主線程的Github代碼

特殊情況:

  • OnApplicationPause:當遊戲暫停時會執行。
  • OnDestroy:物體被刪除時會執行。
  • OnApplicationQuit:當玩家退出遊戲時會執行。

Unity 的生命屬性(Attributes):
在 Unity 中,
你可以掛一些帽子(Attributes)在代碼上面
這樣就可以在沒有把代碼放在場景的情況下也可以執行:

[InitializeOnLoad]:讓腳本在編輯器加載時執行。
[ExecuteInEditMode]:讓腳本在編輯模式下也能執行,而不僅僅是在遊戲運行時。
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]:
在SplashScreen之前執行

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]:
在Awake之前執行

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
在Awake之後執行

[RuntimeInitializeOnLoadMethod]
第一個場景完成讀取之後執行

class MyClass
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    static void OnBeforeSplashScreen()
    {
        Debug.Log("Before SplashScreen is shown and before the first scene is loaded.");
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void OnBeforeSceneLoad()
    {
        Debug.Log("First scene loading: Before Awake is called.");
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    static void OnAfterSceneLoad()
    {
        Debug.Log("First scene loaded: After Awake is called.");
    }

    [RuntimeInitializeOnLoadMethod]
    static void OnRuntimeInitialized()
    {
        Debug.Log("Runtime initialized: First scene loaded: After Awake is called.");
    }
}

以下是測試完整的生命流程的代碼:

using UnityEngine;
using System.Collections;

public class LifecycleMonitor : MonoBehaviour
{
    // 在這裡定義變量
    public float speed = 5f;
    private Rigidbody rb;

    // Awake 函數在所有其他指令之前執行
    void Awake()
    {
        // 初始化變量
        rb = GetComponent<Rigidbody>();
        Debug.Log("Awake: 初始化變量");
    }

    // OnEnable 函數在物體被啟用時執行
    void OnEnable()
    {
        Debug.Log("OnEnable: 物體被啟用");
    }

    // Start 函數在第一次畫面更新之前執行
    void Start()
    {
        Debug.Log("Start: 遊戲開始");
        // 啟動協程
        StartCoroutine(MyCoroutine());
    }

    // Update 函數每一幀都會執行一次
    void Update()
    {
        // 獲取玩家輸入
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        // 計算移動方向
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

        // 移動物體
        rb.AddForce(movement * speed);
        
        Debug.Log("Update: 每一幀都在執行");
    }

    // FixedUpdate 函數每一幀都會執行一次,通常用來處理物理運算
    void FixedUpdate()
    {
        // 在這裡處理物理運算
        Debug.Log("FixedUpdate: 處理物理運算");
    }

    // LateUpdate 函數在所有 Update 函數執行完之後執行
    void LateUpdate()
    {
        Debug.Log("LateUpdate: 在所有 Update 之後執行");
    }

    // OnApplicationPause 函數在遊戲暫停時執行
    void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            Debug.Log("OnApplicationPause: 遊戲暫停");
        }
        else
        {
            Debug.Log("OnApplicationPause: 遊戲恢復");
        }
    }

    // OnDestroy 函數在物體被刪除時執行
    void OnDestroy()
    {
        Debug.Log("OnDestroy: 物體被刪除");
    }

    // OnApplicationQuit 函數在玩家退出遊戲時執行
    void OnApplicationQuit()
    {
        Debug.Log("OnApplicationQuit: 玩家退出遊戲");
    }

    // 自定義協程
    IEnumerator MyCoroutine()
    {
        Debug.Log("Coroutine: 開始協程");
        yield return new WaitForSeconds(2);
        Debug.Log("Coroutine: 等待了 2 秒");
        yield return new WaitForSeconds(2);
        Debug.Log("Coroutine: 又等待了 2 秒");
    }
}


上一篇
Unity 座標之力 - 一次搞懂 Unity 全部座標系統的轉換
下一篇
Unity 語法糖 - 請注意你的 [Attribute]!
系列文
Unity黑科技揭秘:30個專業遊戲開發者必知的開發技巧25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言